/************************************************************************
 *  Copyright (c) 2011-2012 HONG LEIMING.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to permit
 * persons to whom the Software is furnished to do so, subject to the
 * following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
 * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/
package net.zbus.rpc;

import java.nio.charset.Charset;

import net.zbus.BusClient;
import net.zbus.ClientPool;
import net.zbus.ZBusException;
import net.zbus.ZMsg;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;

public class Rpc { 
	public static final String LANG_JAVA = "java";
	public static final String LANG_KEY = "__client__";
	public static final Charset DEFAULT_ENCODING = Charset.forName("UTF8");

	private ClientPool clientPool;

	private String service;
	private String token = "";
	private String module = "";
	
	private Charset encoding;
	private int timeout = 2500;
	private String remoteLanguage = LANG_JAVA;

	public Rpc(ClientPool clientPool, String service) {
		this(clientPool, service, "");
	}
	
	public Rpc(ClientPool clientPool, String service, String module) {
		this.clientPool = clientPool;
		this.service = service;
		this.encoding = DEFAULT_ENCODING;
		this.module = module;
	}
	

	public Object invoke(String method, Object... args) {

		JSONObject req = new JSONObject();
		req.put("module", this.module);
		req.put("method", method);
		req.put("params", JSON.toJSON(args));
		req.put(LANG_KEY, remoteLanguage);
		ZMsg msg = new ZMsg();
		if (LANG_JAVA.equals(remoteLanguage)) {
			msg.pushBack(JSON.toJSONBytes(req,
					SerializerFeature.WriteMapNullValue,
					SerializerFeature.WriteClassName));
		} else {
			msg.pushBack(JSON.toJSONBytes(req,
					SerializerFeature.WriteMapNullValue));
		}
		BusClient client = null;
		try {
			client = this.clientPool.borrowClient();
			msg = client.request(this.service, this.token, msg, this.timeout);
		} finally {
			if (client != null) {
				clientPool.returnClient(client);
			}
		}
		if (msg == null) {
			throw new ZBusException("json rpc request timeout");
		}
		if (msg.frameSize() != 2) {
			throw new ZBusException("json rpc format error[2 frames required]");
		}
		String status = msg.popFrontStr();
		if (status.equals("200")) {
			try {
				String text = new String(msg.popFront(), this.encoding);
				JSONObject res = (JSONObject) JSON.parse(text);
				if (res.containsKey("result"))
					return res.get("result");
				else if (res.containsKey("error"))
					throw new ZBusException(res.getString("error"));
				else
					throw new ZBusException("return json invalid");
			} catch (Exception e) {
				throw new ZBusException(e.getMessage());
			}
		} else {
			throw new ZBusException(msg.popFrontStr());
		}
	}

	public String getService() {
		return service;
	}

	public void setService(String service) {
		this.service = service;
	}

	public String getToken() {
		return token;
	}

	public void setToken(String token) {
		this.token = token;
	}

	public Charset getEncoding() {
		return encoding;
	}

	public void setEncoding(Charset encoding) {
		this.encoding = encoding;
	}

	public int getTimeout() {
		return timeout;
	}

	public void setTimeout(int timeout) {
		this.timeout = timeout;
	}

	
	
	public String getModule() {
		return module;
	}

	public void setModule(String module) {
		this.module = module;
	}
	
	public Rpc module(String module) {
		this.module = module;
		return this;
	}

	public String getRemoteLanguage() {
		return remoteLanguage;
	}

	public void setRemoteLanguage(String remoteLanguage) {
		this.remoteLanguage = remoteLanguage;
	}
}
